package com.qth.valet.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qth.valet.common.enums.PropertyEnum;
import com.qth.valet.common.listener.OrderCreatedEvent;
import com.qth.valet.common.theadlocal.UserOrDriverIdHolder;
import com.qth.valet.common.utils.*;
import com.qth.valet.entity.po.*;
import com.qth.valet.entity.property.Commission;
import com.qth.valet.entity.req.DriverFinishOrderReq;
import com.qth.valet.entity.vo.InsuranceSucVo;
import com.qth.valet.entity.vo.InsuranceVo;
import com.qth.valet.entity.vo.OrdersDetail;
import com.qth.valet.entity.vo.OrdersPending;
import com.qth.valet.mapper.OrdersMapper;
import com.qth.valet.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements OrdersService {

    private static final String LOCK_KEY = "order_lock:";
    private static final int LOCK_EXPIRE_TIME = 10; // 锁过期时间（秒）

    private final StringRedisTemplate stringRedisTemplate;

    private final DriversService driversService;

    private final ApplicationEventPublisher applicationEventPublisher;

    private final OrderHistoryService orderHistoryService;

    private final PropertyService propertyService;

    private final NoticeDriverService noticeDriverService;

    private final DriverBalanceDetailService driverBalanceDetailService;

    private final InsuranceService insuranceService;

    @Value("${order.mindistance}")
    private double mindistance;

    @Value("${order.min-insurance-amount}")
    private Integer minInsuranceAmount;

    @Value("${insurance.company}")
    private String insuranceCompany;

    @Override
    public List<OrdersPending> getPendingOrder(Double longitude, Double latitude, int status) {
        return this.getBaseMapper().getPendingOrder(longitude, latitude, StatusConstants.ORDER_WAIT);
    }

    @Override
    @Transactional
    public ResponseResult<Void> grabOrder(Integer driverId, String orderId) {
        // 查询司机有没有司机创建的订单正在服务
        List<Orders> orders = checkDriverOnline(driverId);
        if (!orders.isEmpty()) {
            return ResponseResult.fail("您有正在进行的订单，请完成后再次抢单");
        }

        Driver driver = driversService.getById(driverId);
        // 查询司机账户余额
        if (driver == null) {
            throw new RuntimeException("参数异常");
        } else if (driver.getAccountBalance() < minInsuranceAmount) {
            return ResponseResult.fail("您的账户余额不足，请充值后再抢单");
        }

        Double balance = insuranceService.getBalance();
        if(balance == null || balance < minInsuranceAmount){
            return ResponseResult.fail("保险账户余额不足，请联系客服处理。");
        }

        String lockKey = LOCK_KEY + orderId;
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
        if (lock == null || !lock) {
            return ResponseResult.fail("抢单失败，订单已被其他司机抢单");
        }

        try {
            // 查询订单信息
            Orders byId = this.getById(orderId);
            if (byId == null) {
                return ResponseResult.fail("订单不存在");
            }

            if (byId.getStatus() != StatusConstants.ORDER_WAIT) {
                return ResponseResult.fail("抢单失败，订单已被其他司机抢单");
            }

            // 更新订单状态和司机ID
            byId.setStatus(StatusConstants.ORDER_GET);
            byId.setDriverId(driverId);
            byId.setReceiveTime(new Date());
            this.updateById(byId);

            // 更新司机状态
            driversService.updateSts(driverId, StatusConstants.DRIVER_OFFLINE);

            return ResponseResult.success("抢单成功");
        } finally {
            // 释放锁
            stringRedisTemplate.delete(lockKey);
        }
    }

    @Override
    public OrdersDetail getOrderDetail(String orderId) {
        return this.getBaseMapper().getOrderDetail(orderId);
    }

    @Override
    public IPage<Orders> getAllOrder(Integer driverId, Page<Orders> page, Integer status) {
        System.out.println("🔍 查询司机订单 - 司机ID: " + driverId + ", 状态: " + status);
        return this.getBaseMapper().getAllOrder(driverId, page, status);
    }

    @Override
    public Orders getOrderById(String orderId) {
        return this.getBaseMapper().getOrderById(orderId);
    }

    @Override
    public void publishOrder(Orders order) {
        applicationEventPublisher.publishEvent(new OrderCreatedEvent(this, order));
    }

    @Override
    public List<Orders> getAllPendingOrder() {
        LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Orders::getStatus, StatusConstants.ORDER_WAIT).orderByAsc(Orders::getId);
        return this.list(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult<Void> updateOrderSts(Orders orders) {
        Orders dbOrder = this.getBaseMapper().selectById(orders.getId());
        if (dbOrder == null) {
            throw new RuntimeException("参数异常");
        } else if (dbOrder.getStatus() == StatusConstants.ORDER_REFUNDED) {
            throw new RuntimeException("参数异常");
        } else if (dbOrder.getStatus() == StatusConstants.ORDER_PENDING_PAY && orders.getStatus() == StatusConstants.ORDER_CANCEL) {
            return ResponseResult.fail("服务已完成，请刷新订单进行支付");
        }

        LambdaUpdateWrapper<Orders> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Orders::getId, orders.getId()).set(Orders::getStatus, orders.getStatus());
        String operate = null;
        if (orders.getStatus() == StatusConstants.ORDER_DRIVER_WAIT) {
            updateWrapper.set(Orders::getReachTime, new Date());
            operate = "司机到达用户地点";
        } else if (orders.getStatus() == StatusConstants.ORDER_START) {
            updateWrapper.set(Orders::getStartTime, new Date());
            operate = "司机开始行程";

            //判断保险账户余额
            Double balance = insuranceService.getBalance();
            if(balance == null || balance < minInsuranceAmount){
                return ResponseResult.fail("保险账户余额不足，请联系客服处理。");
            }

            //判断用户手机号是否为空
            if (dbOrder.getUserPhone() == null) {
                log.error("用户手机号为空，订单ID: {}", dbOrder.getId());
                return ResponseResult.fail("用户手机号信息不完整，无法购买保险");
            }

            //购买保险
            Driver driver = driversService.getById(dbOrder.getDriverId());
            ResponseResult responseResult = insuranceService.orderUpOne(new InsuranceVo()
                    .setOrder_id(dbOrder.getId())
                    .setUser(Long.valueOf(dbOrder.getUserPhone()))
                    .setJie_time(Instant.now().getEpochSecond())
                    .setSiji(driver.getSurname() + driver.getName())
                    .setSiji_phone(Long.valueOf(driver.getPhone()))
                    .setStart(dbOrder.getCurrentAddr())
                    .setCompany(insuranceCompany));
            if(responseResult.getCode() == 200){
                Integer id = Integer.valueOf(responseResult.getData().toString());
                updateWrapper.set(Orders::getInsurance, id);
            }else{
                return responseResult;
            }
        }

        boolean update = this.update(updateWrapper);
        if (update) {
            if (Arrays.asList(StatusConstants.ORDER_CANCEL, StatusConstants.ORDER_REFUND_PENDING).contains(orders.getStatus())) {
                // 用户操作
                if (orders.getStatus() == StatusConstants.ORDER_CANCEL) {
                    operate = "用户取消订单";
                } else if (orders.getStatus() == StatusConstants.ORDER_REFUND_PENDING) {
                    operate = "用户申请退款";
                }
                orderHistoryService.saveOrderHistory(orders.getId(), dbOrder.getUserId(), null, operate);
            } else {
                // 司机操作
                orderHistoryService.saveOrderHistory(orders.getId(), null, dbOrder.getDriverId(), operate);
            }
        }
        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void driverFinishOrder(DriverFinishOrderReq req) {
        // 查询订单信息
        Orders order = this.getById(req.getOrderId());
        if (order == null) {
            // 订单不存在
            throw new RuntimeException("参数异常");
        } else if (!Objects.equals(order.getDriverId(), UserOrDriverIdHolder.getDriverId())) {
            // 司机ID不匹配
            throw new RuntimeException("参数异常");
        } else if (order.getStatus() == StatusConstants.ORDER_PENDING_PAY || order.getStatus() == StatusConstants.ORDER_FINISH) {
            // 状态已经是待支付或者已完成，不能再做修改
            throw new RuntimeException("参数异常");
        }
        Date date = new Date();
        LambdaUpdateWrapper<Orders> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .eq(Orders::getId, req.getOrderId())
                .set(Orders::getEndTime, date)             // 送达时间
                .set(Orders::getPayType, req.getPayType()) // 支付方式
                .set(Orders::getStatus,
                        req.getPayType() == StatusConstants.ORDER_PAY_TYPE_ONLINE
                        ? StatusConstants.ORDER_PENDING_PAY : StatusConstants.ORDER_FINISH
                )   // 订单状态(线上支付：待支付；线下支付，已完成)
        ;

        String operate = req.getPayType() == StatusConstants.ORDER_PAY_TYPE_ONLINE ? "司机结束行程" : "司机完成行程";

        // 线下支付
        if (req.getPayType() != StatusConstants.ORDER_PAY_TYPE_ONLINE) {
            updateWrapper.set(Orders::getFinishTime, date);
        }
        // 司机操作
        orderHistoryService.saveOrderHistory(order.getId(), null, UserOrDriverIdHolder.getDriverId(), operate);
        // 更新订单
        this.update(updateWrapper);

        // 查询司机信息
        Driver driver = driversService.getById(UserOrDriverIdHolder.getDriverId());
        if (driver == null) {
            throw new RuntimeException("参数异常");
        }

        // 如果是线下支付，在此处插入明细。否则在用户支付回调里插入明细。
        if (req.getPayType() != StatusConstants.ORDER_PAY_TYPE_ONLINE) {
            DriverBalanceDetail driverBalanceDetail = new DriverBalanceDetail();
            driverBalanceDetail.setDriverId(driver.getId());                               // 当前司机ID
            driverBalanceDetail.setOrderId(order.getId());
            driverBalanceDetail.setBalanceType(StatusConstants.BALANCE_TYPE_DRIVE_INCOME_OFFLINE); // 代驾收入(线下)
            driverBalanceDetail.setAmount(order.getPrice());
            driverBalanceDetail.setRemainingBalance(driver.getAccountBalance());           // 线下支付，账户余额不变
            driverBalanceDetailService.save(driverBalanceDetail);
        }

        // 司机不是会员，平台要抽成
        if (driver.getVipType() == StatusConstants.DRIVER_VIP_NON || driver.getVipExpireTime() == null || driver.getVipExpireTime().before(new Date())) {
            // 查询抽成比例信息
            String commissionKey = PropertyEnum.COMMISSION.getKey();
            Property commissionObject = propertyService.getById(commissionKey);
            Commission commission = (Commission) PropertyEnum.COMMISSION.parse(commissionObject.getTheValue());

            // 平台部分
            long platformAmount = (long) Math.ceil((commission.getPlatform() * order.getPrice()));
            // 上线部分
            long fatherAmount = (long) Math.ceil((commission.getFather() * order.getPrice()));
            // 需要扣减的金额
            long sum = platformAmount + fatherAmount;

            // 当前司机 扣减余额 + 插入明细 + 通知
            // 更新司机余额
            driversService.updateDriverBalanceMinus(driver.getId(), sum);
            // 插入明细
            DriverBalanceDetail driverBalanceDetail = new DriverBalanceDetail();
            driverBalanceDetail.setDriverId(driver.getId());                               // 当前司机ID
            driverBalanceDetail.setOrderId(order.getId());
            driverBalanceDetail.setBalanceType(StatusConstants.BALANCE_TYPE_PLATFORM_EXPENDITURE); // 平台抽成支出
            driverBalanceDetail.setAmount(sum);
            // 余额已修改，直接查
            driverBalanceDetail.setRemainingBalance(driversService.getById(driver.getId()).getAccountBalance());
            log.info("更新司机账户余额，司机ID：{}，减少{}", driver.getId(), sum);

            // 通知
            noticeDriverService.save(new NoticeDriver()
                    .setDriverId(driver.getId())
                    .setType(StatusConstants.NOTICE_PLATFORM_EXPENDITURE)
                    .setContent("单号“" + order.getId() + "”的代驾订单，平台抽成" + AmountUtil.formatAmount(sum) + "元")
            );
            // 将司机的通知读取状态改为未读
            driversService.updateNoticeReadStatus(driver.getId(), false);

            // 插入一条司机余额明细
            driverBalanceDetailService.save(driverBalanceDetail);

            // 上线司机 增加余额 + 插入明细 + 通知
            if (driver.getPid() != null && driver.getPid() != 0) {
                Integer pid = driver.getPid();
                // 查询上线司机信息
                Driver father = driversService.getById(pid);
                // 司机不在线 不给提成
                if (father != null && CommonUtil.isDriverOnline(father.getStatus())) {
                    // 更新司机余额
                    driversService.updateDriverBalancePlus(pid, fatherAmount);
                    // 插入明细
                    DriverBalanceDetail dbt = new DriverBalanceDetail();
                    dbt.setDriverId(pid);                               // 上线司机ID
                    dbt.setChildId(driver.getId());                     // 当前司机ID
                    dbt.setOrderId(order.getId());
                    dbt.setBalanceType(StatusConstants.BALANCE_TYPE_CHILD_INCOME); // 上线收入
                    dbt.setAmount(fatherAmount);
                    // 余额已修改，直接查
                    dbt.setRemainingBalance(driversService.getById(pid).getAccountBalance());
                    log.info("更新上线司机账户余额，司机ID：{}，增加{}", pid, fatherAmount);

                    // 插入一条司机余额明细
                    driverBalanceDetailService.save(dbt);

                    // 通知
                    noticeDriverService.save(new NoticeDriver()
                            .setDriverId(father.getId())
                            .setType(StatusConstants.NOTICE_COMMISSION_INCOME)
                            .setContent("您的下线“" + driver.getSurname() + driver.getName() + "”为您带来" + AmountUtil.formatAmount(fatherAmount) + "元的收益")
                    );
                    // 将司机的通知读取状态改为未读
                    driversService.updateNoticeReadStatus(father.getId(), false);
                }

            }
        }
        //完成保险
        double distance = DistanceCalculator.calculateDistance(order.getCurrentLa(), order.getCurrentLo(), order.getTargetLa(), order.getTargetLo());
        insuranceService.orderSuc(new InsuranceSucVo()
                .setId(order.getInsurance())
                .setJvli((int) distance)
                .setOrder_finish(Instant.now().getEpochSecond())
                .setEnd(order.getTargetAddr()));
    }

    @Override
    public void selectDriverBySchedule() {
        List<Orders> allPendingOrder = this.getAllPendingOrder();
        for (Orders order : allPendingOrder) {
            log.info("有待接单的订单：" + order.getId());
            applicationEventPublisher.publishEvent(new OrderCreatedEvent(this, order));
            /*Driver driver = selectDriver(order);
            if (driver != null) {
                log.info("司机" + driver.getId() + "接单：" + order.getId());
                ResponseResult result = this.grabOrder(driver.getId(), order.getId());
                if(result.getStatus()){
                    applicationEventPublisher.publishEvent(new OrderCreatedEvent(this, order));
                }
            }*/
        }
    }

    @Override
    public Driver selectDriver(Orders order) {
        LambdaQueryWrapper<Driver> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Driver::getStatus, StatusConstants.DRIVER_ONLINE);
        List<Driver> list = driversService.list(queryWrapper);
        double minDistance = mindistance;
        double lo;
        double la;
        Driver bestCandidate = null;
        for (Driver driver : list) {
            Object lo_la = stringRedisTemplate.opsForHash().get(StatusConstants.DRIVER_LO_LA, driver.getId().toString());
            if (lo_la != null) {
                JSONObject jsonObject = JSONUtil.parseObj(lo_la);
                lo = jsonObject.getDouble(StatusConstants.DRIVER_LO);
                la = jsonObject.getDouble(StatusConstants.DRIVER_LA);
            } else {
                continue;
            }
            double distance = DistanceCalculator.calculateDistance(
                    order.getCurrentLa(), order.getCurrentLo(),
                    la, lo
            );
            if (distance < minDistance) {
                minDistance = distance;
                bestCandidate = driver;
            }
        }

        return bestCandidate;
    }

    /**
     * 判断当前司机是否正在接单中
     */
    @Override
    public List<Orders> checkDriverOnline(Integer driverId) {
        LambdaQueryWrapper<Orders> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.apply("(status = {0} or status = {1} or status = {2}) and driver_id = {3}",
                StatusConstants.ORDER_GET, StatusConstants.ORDER_DRIVER_WAIT, StatusConstants.ORDER_START, driverId);

        return this.list(lambdaQueryWrapper);
    }

    @Override
    public void updatePayVersion(String orderId) {
        LambdaUpdateWrapper<Orders> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(Orders::getId, orderId)
                .setSql("pay_version = pay_version + 1");

        this.update(lambdaUpdateWrapper);
    }
}
